package com.example.demo.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.domain.conts.ConstDataType;
import com.example.demo.domain.model.Parameter;
import com.example.demo.domain.model.ParameterAttributeValue;
import com.example.demo.domain.model.ParameterDefinition;
import com.example.demo.domain.vo.ParameterVO;
import com.example.demo.domain.vo.ParameterValueVO;
import com.example.demo.service.ParameterClientService;
import com.example.demo.service.ParameterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ParameterClientServiceImpl<T extends ParameterValueVO> implements ParameterClientService<T> {
    @Autowired
    private ParameterService parameterService;

    @Override
    public ParameterVO<T> getParameterByCode(String code, Class<T> clazz) {
        Parameter parameter = parameterService.getParameterByCode(code);
        ParameterVO<T> parameterVO = new ParameterVO<>();
        parameterVO.setCode(parameter.getCode());
        parameterVO.setName(parameter.getName());
        parameterVO.setDescription(parameter.getDescription());

        List<T> parameterValueVOS = getParameterValueList(clazz, parameter);
        parameterVO.setParameterValue(parameterValueVOS);
        return parameterVO;
    }

    @Override
    public List<T> getParameterValueByParentCode(String parentCode, Class<T> clazz) {
        Parameter parameter = parameterService.getParameterByCode(parentCode);
        return getParameterValueList(clazz, parameter);
    }

    @Override
    public T getParameterValueByCode(String parentCode, String code, Class<T> clazz) {
        Parameter parameter = parameterService.getParameterByCode(parentCode);
        List<ParameterDefinition> pds = parameter.getParameterDefinitions();
        Optional<com.example.demo.domain.model.ParameterValue> one = parameter.getParameterValues().stream().filter(pv -> pv.getCode().equals(code)).findFirst();
        com.example.demo.domain.model.ParameterValue parameterValue = one.orElse(null);
        if (parameterValue == null) {
            return null;
        }
        return getParameterValue(clazz, parameterValue, pds);
    }

    private List<T> getParameterValueList(Class<T> clazz, Parameter parameter) {
        Assert.notNull(clazz, "Assert clazz isNull");
        Assert.notNull(parameter, "Assert parameter isNull");

        List<com.example.demo.domain.model.ParameterValue> parameterValues = parameter.getParameterValues();
        List<ParameterDefinition> pds = parameter.getParameterDefinitions();
        return parameterValues.stream().map(pv -> this.getParameterValue(clazz, pv, pds)).collect(Collectors.toList());
    }

    private T getParameterValue(Class<T> clazz, com.example.demo.domain.model.ParameterValue pv, List<ParameterDefinition> pds) {
        Assert.notNull(clazz, "Assert clazz isNull");
        Assert.notNull(pv, "Assert ParameterValue isNull");
        Assert.notNull(pds, "Assert ParameterDefinition isNull");

        JSONObject jsonObj = (JSONObject) JSONObject.toJSON(pv);
        Map<String, ParameterAttributeValue> map = pv.getParameterAttributeValues().stream().collect(Collectors.toMap(pav -> pav.getAttributeCode() + pav.getDataType(), attributeValue -> attributeValue));
        for (ParameterDefinition pd : pds) {
            String key = pd.getCode() + pd.getDatatype();
            ParameterAttributeValue pav = map.get(key);
            if (pav == null) {
                break;
            }
            switch (pav.getDataType()) {
                case ConstDataType.STRING:
                    jsonObj.put(pd.getName(), pav.getStringValue());
                    break;
                case ConstDataType.DOUBLE:
                    jsonObj.put(pd.getName(), pav.getDoubleValue());
                    break;
                case ConstDataType.DATETIME:
                    jsonObj.put(pd.getName(), pav.getDatetimeValue());
                    break;
                case ConstDataType.BOOLEAN:
                    jsonObj.put(pd.getName(), pav.getBooleanValue());
                    break;
                case ConstDataType.INTEGER:
                    jsonObj.put(pd.getName(), pav.getIntValue());
                    break;
                case ConstDataType.TEXT:
                    jsonObj.put(pd.getName(), pav.getTextValue());
                    break;
                case ConstDataType.JOSNLIST:
                    JSONArray value =(JSONArray) JSON.parse(pav.getTextValue());
                    jsonObj.put(pd.getName(), value);
                    break;
            }
        }
        return JSON.parseObject(jsonObj.toJSONString(), clazz);
    }
}
